//============================================================================
// Name        : effects_manager.cpp
// Author      : dfoelber
//============================================================================

// INCLUDES //
#include "effects_manager.h"
#include "util.h"
#include <iostream>
#include <sstream>
//////////////

EffectsManager* EffectsManager::instance = NULL;

/**
* Private Constructor
**/
EffectsManager::EffectsManager()
{
}

/**
* Deconstructor
**/
EffectsManager::~EffectsManager()
{
	for( int i = 0; i < (int)effects.size(); i++ )
	{
		delete effects[i];
	}
}

/**
* Singleton accessor
* 
* @return Singleton instance
**/
EffectsManager* EffectsManager::GetManager()
{
	if( instance == NULL )
	{
		instance = new EffectsManager();
	}
	
	return instance;
}

/**
* Notifies of time passing.
* 
* @param The amount of time, in milliseconds, that passed since the last call
**/
void EffectsManager::TimePassed( int ms )
{
	for( int i = 0; i < (int)effects.size(); i++ )
	{
		if( effects[i]->state != EFFECT_STATE_LOADED )
		{
			effects[i]->lifetime += ms;
		}
	}
	
	//CheckPrereqs();
}

/**
* Adds a listener.
* 
* @param listener The listener
**/
void EffectsManager::AddListener(EffectsListener* listener)
{
	listeners.push_back(listener);
}

/**
* Processes the card and notifies listeners.
* 
* @param info The card info that is activated
**/
void EffectsManager::CardActivated(CardInfo* info)
{
	vector<Effect*> copies;
	for( int i = 0; i < (int)info->GetEffects().size(); i++ )
	{
		int level = info->GetEffects()[i]->level;
		
		vector<string> prereqs = Util::Split(info->GetEffects()[i]->GetPrereq(), '|');
		bool passed = true;
		for( int p = 0; p < (int)prereqs.size(); p++ )
		{
			string prereq = "";
			vector<string> tokens = Util::Split(prereqs[p], ',');
			if( tokens.size() > 1 && tokens[0] == EFFECT_PREREQ_MIN_LEVEL )
			{
				if( atoi(tokens[1].c_str()) > level )
				{
					passed = false;
				}
			}
		}
		
		if( passed )
		{
			if( info->GetEffects()[i]->GetId() == EFFECT_DIRECT_DAMAGE && 
					(int)((DirectDamageEffect*)info->GetEffects()[i])->GetDamage().size() < level )
				level = ((DirectDamageEffect*)info->GetEffects()[i])->GetDamage().size();
			else if( info->GetEffects()[i]->GetId() == EFFECT_DAMAGE_OVER_TIME && 
					(int)((DoTEffect*)info->GetEffects()[i])->GetDamage().size() < level )
				level = ((DoTEffect*)info->GetEffects()[i])->GetDamage().size();
			else if( info->GetEffects()[i]->GetId() == EFFECT_ALIGNMENT_CHANGE && 
					(int)((AlignmentChangeEffect*)info->GetEffects()[i])->GetChange().size() < level )
				level = ((AlignmentChangeEffect*)info->GetEffects()[i])->GetChange().size();
			
			if( level <= 0 )
				continue;
			else info->GetEffects()[i]->level = level - 1;
		
			copies.push_back(info->GetEffects()[i]->Copy());
		}
	}
	
	for( int i = 0; i < (int)copies.size(); i++ )
	{
		copies[i]->associations.clear();
		int random = 0;
		bool contains;
		do
		{
			contains = false;
			random = rand() % 10000;
			for( int e = 0; e < (int)copies.size(); e++ )
			{
				if( copies[e]->uid == random )
					contains = true;
			}
		}
		while( contains );
		
		copies[i]->uid = random;
		
		// if a local prereq, set to location of effect
		vector<string> prereqs = Util::Split(copies[i]->GetPrereq(), '|');
		string pres = "";
		for( int p = 0; p < (int)prereqs.size(); p++ )
		{
			string prereq = "";
			vector<string> tokens = Util::Split(prereqs[p], ',');
			if( tokens.size() > 1 && tokens[0] == EFFECT_PREREQ_LOCAL )
			{
				copies[i]->useCastAnimation = false;
				int index;
				stringstream ss(tokens[1]);
				ss >> index;
				ostringstream oss;
				oss << copies[index]->uid;
				copies[i]->associations.insert(pair<string,string>(tokens[1],oss.str()));
			}
			
			for( int t = 0; t < (int)tokens.size(); t++ )
			{
				prereq += tokens[t] + ",";
			}
			prereq.resize(prereq.size() - 1);
			pres += prereq + "|";
		}
		pres.resize(pres.size() - 1);
		copies[i]->SetPrereq(pres);
		
		copies[i]->origin = EFFECT_ORIGIN_PLAYER;
		copies[i]->state = EFFECT_STATE_LOADED;
		effects.push_back( copies[i] );
	}
	
	CheckPrereqs();
}

/**
* Notifies the manager that the effect has changed state.
* 
* @param effect The effect
* @param state The new state
**/
void EffectsManager::ChangeEffectState(Effect* effect, string state)
{
	for( int i = 0; i < (int)effects.size(); i++ )
	{
		if( effects[i] == effect )
		{
			effects[i]->state = state;
			
			for( int l = 0; l < (int)listeners.size(); l++ )
			{
				listeners[l]->EffectStateChanged(effects[i]);
			}
			
			CheckPrereqs();
			return;
		}
	}
}

/**
* Accesses the prerequisites for each effect and starts them if they are ready.
**/
void EffectsManager::CheckPrereqs()
{
	for( int i = 0; i < (int)effects.size(); i++ )
	{
		if( effects[i]->state == EFFECT_STATE_LOADED )
		{
			if( effects[i]->GetPrereq() == EFFECT_PREREQ_NONE )
			{
				effects[i]->state = EFFECT_STATE_READY;
				for( int l = 0; l < (int)listeners.size(); l++ )
				{
					listeners[l]->EffectStateChanged(effects[i]);
				}
			}
			else
			{
				vector<string> prereqs = Util::Split(effects[i]->GetPrereq(), '|');
				bool passed = true;
				for( int p = 0; p < (int)prereqs.size(); p++ )
				{
					vector<string> tokens = Util::Split(prereqs[p], ',');
					if( tokens.size() > 1 && tokens[0] == EFFECT_PREREQ_LOCAL )
					{
						for( int e = 0; e < (int)effects.size(); e++ )
						{
							string id = "-1";
							ostringstream oss;
							oss << effects[e]->uid;
							
							id = effects[i]->associations[tokens[1]];
							
							if( tokens.size() > 3 && oss.str() == id )
							{
								int lifetime;
								stringstream ss(tokens[2]);
								ss >> lifetime;
								if( effects[e]->lifetime < lifetime || 
									(tokens[3] == EFFECT_STATE_READY && effects[e]->state == EFFECT_STATE_LOADED) ||
									(tokens[3] == EFFECT_STATE_ANIMATION_STARTED && effects[e]->state == EFFECT_STATE_READY) ||	
									(tokens[3] == EFFECT_STATE_ANIMATION_STARTED && effects[e]->state == EFFECT_STATE_LOADED) ||
									(tokens[3] == EFFECT_STATE_ANIMATION_DONE && effects[e]->state == EFFECT_STATE_ANIMATION_STARTED) || 
									(tokens[3] == EFFECT_STATE_ANIMATION_DONE && effects[e]->state == EFFECT_STATE_LOADED) ||
									(tokens[3] == EFFECT_STATE_ANIMATION_DONE && effects[e]->state == EFFECT_STATE_READY) )
								{
									passed = false;
									break;
								}
							}
							
							oss.clear();
						}
					}
				}
				
				if( passed )
				{
					effects[i]->state = EFFECT_STATE_READY;
					for( int l = 0; l < (int)listeners.size(); l++ )
					{
						listeners[l]->EffectStateChanged(effects[i]);
					}
				}
			}
		}
	}
}
